package com.lcx.demo.nioday01;

import org.junit.Test;

import java.io.*;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CharsetEncoder;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.Map;
import java.util.Set;

/*
*一、通道（channel）：用于源节点与目标节点的连接，在Java NIO中负责缓冲区中数据的传输。Channel 本身不存储数据，因此需要配合缓冲区进行传输。
*
* 二、通道的主要实现类
*  java.nio.Channel 接口
*       |--FileChannel
*       |--SocketChannel
*       |--ServerSocketChannel
*       |--DatagramChannel
*
* 三、获取通道
* 1.java 针对支持通道的类提供了getChannle()方法
*       本地IO
*       FileInputStream/FileOutputStream
*       RandomAccessFile
*
*       网路IO
*       Socket
*       ServerSocket
*       DatagramChannel
*
* 2.在jdk1.7中的nio.2 针对各个通道提供了静态方法 open（）
*
* 3.在jdk1.7中的nio.2的Files工具类的newByteChannel()
*
* 四.通道之间的数据传输
*  transferFrom()
*  transferTo()
*
* 五、分散（Scatter） 与聚集（Gather）
* 分散读取（Scattering Reads）：将通道中的数据分散到多个缓冲区中
* 聚集写入 （Gathering Writes）：将多个缓冲区中的数据聚集到通道中
*
* 六、字符集：Charset
* 编码：字符串-》字节数组
* 解码：字节数组-》字符串
*
*/
public class TestChannel {

    //字符集
    @Test
    public void test6() throws CharacterCodingException {
        Charset cs1 = Charset.forName("GBK");
        //获取编码器
        CharsetEncoder ce = cs1.newEncoder();

        //获取解码器
        CharsetDecoder cd = cs1.newDecoder();

        CharBuffer charBuffer = CharBuffer.allocate(1024);
        charBuffer.put("测试字符集");
        charBuffer.flip();

        //编码
        ByteBuffer encode = ce.encode(charBuffer);

        for (int i = 0; i < 10; i++) {
            System.out.println(encode.get());
        }

        //解码
        encode.flip();
        CharBuffer decode = cd.decode(encode);
        System.out.println(decode.toString());
        System.out.println("-----------------");

        //使用utf-8解码会乱码
        Charset charset = Charset.forName("UTF-8");
        encode.flip();
        CharBuffer charBuffer1 = charset.decode(encode);
        System.out.println(charBuffer1.toString());
    }

    @Test
    public void test5(){
        Map<String, Charset> map = Charset.availableCharsets();
        Set<Map.Entry<String,Charset>> set = map.entrySet();

        for (Map.Entry<String, Charset> entry :
                set) {
            System.out.println(entry.getKey()+"="+entry.getValue());
        }
    }

    //分散与聚集
    @Test
    public void test4() throws IOException {
        RandomAccessFile raf1 = new RandomAccessFile("1.txt","rw");

        //1.获取通道
        FileChannel channel = raf1.getChannel();

        System.out.println(raf1.length());
        ByteBuffer[] bufs = new ByteBuffer[(int) (raf1.length()/1024+1)];
        for (int i = 0,j=0; i < raf1.length(); i=i+1024,j++) {
            bufs[j] = ByteBuffer.allocate(1024);
        }
        //2.分配指定大小的缓冲区
//        ByteBuffer buf1 = ByteBuffer.allocate(1024);
//        ByteBuffer buf2 = ByteBuffer.allocate(1024);

        //分散读取
//        ByteBuffer[] bufs = {buf1,buf2};
        channel.read(bufs);

        for (ByteBuffer byteBuffer:bufs){
            byteBuffer.flip();
            System.out.println(new String(byteBuffer.array(),0,byteBuffer.limit()));
        }
//        System.out.println(new String(bufs[0].array(),0,bufs[0].limit()));
//        System.out.println("--------------");
//        System.out.println(new String(bufs[(int) (raf1.length()/1024)].array(),0,bufs[1].limit()));

        //聚集写入
        RandomAccessFile raf2 = new RandomAccessFile("2.txt","rw");
        FileChannel channel2 = raf2.getChannel();

        channel2.write(bufs);
    }

    //通道之间的数据传输(直接缓冲区）
    @Test
    public void test3() throws IOException {
        FileChannel inChannel = FileChannel.open(Paths.get("D:\\安装jenkins及配置.zip"), StandardOpenOption.READ);
        FileChannel outCHannel = FileChannel.open(Paths.get("D:\\安装jenkins及配置2.zip"),StandardOpenOption.WRITE,StandardOpenOption.READ,StandardOpenOption.CREATE);

//        inChannel.transferTo(0,inChannel.size(),outCHannel);
        outCHannel.transferFrom(inChannel,0,outCHannel.size());

        inChannel.close();
        outCHannel.close();
    }

    //使用直接缓冲区完成文件的复制（内存映射文件）
    @Test
    public void test2() throws IOException {
        long start = System.currentTimeMillis();

        FileChannel inChannel = FileChannel.open(Paths.get("D:\\安装jenkins及配置.zip"), StandardOpenOption.READ);
        FileChannel outCHannel = FileChannel.open(Paths.get("D:\\安装jenkins及配置2.zip"),StandardOpenOption.WRITE,StandardOpenOption.READ,StandardOpenOption.CREATE);

        //内存映射文件
        MappedByteBuffer inMappedBuf = inChannel.map(FileChannel.MapMode.READ_ONLY,0,inChannel.size());
        MappedByteBuffer outMappedBuf = outCHannel.map(FileChannel.MapMode.READ_WRITE, 0, inChannel.size());

        //直接对缓冲区进行数据读写操作
        byte[] dst = new byte[inMappedBuf.limit()];
        inMappedBuf.get(dst);
        outMappedBuf.put(dst);

        inChannel.close();
        outCHannel.close();

        long end = System.currentTimeMillis();

        System.out.println("耗费时间为："+(end-start));



    }

    //1.利用通道完成文件的复制(非直接缓冲区）
    @Test
    public void test1() throws IOException {
        long start = System.currentTimeMillis();
//        FileInputStream fis = new FileInputStream("./src/1.jpg");
        FileInputStream fis = new FileInputStream("D:\\安装jenkins及配置.zip");
//        FileOutputStream fos = new FileOutputStream("2.jpg");
        FileOutputStream fos = new FileOutputStream("D:\\安装jenkins及配置2.zip");

        //获取通道
        FileChannel inChannel = fis.getChannel();
        FileChannel outChannel = fos.getChannel();

        //分配缓冲区
        ByteBuffer buf = ByteBuffer.allocate(1024);

        //将通道中的数据存入缓冲区中
        while(inChannel.read(buf) != -1){
            buf.flip(); //切换读取数据模式
            //将缓冲区中的数据写入通道中
            outChannel.write(buf);
            buf.clear(); //清空缓冲区
        }

        outChannel.close();
        inChannel.close();
        fos.close();
        fis.close();

        long end = System.currentTimeMillis();

        System.out.println("耗费时间为："+(end-start));
    }
}
